/******************************************************************
Copyright © PsuCtl 2017~2024. All rights reserved.
* @projectName   PsuCtl
* @author        Raiiwhen
* @email         masterraii@icloud.com
*******************************************************************/

#include <stdio.h>
#include <string.h>
#include "argtable3/argtable3.h"
#include "esp_console.h"
#include "esp_log.h"
#include "PMBus.h"

smbus_info_t g_smbus;
static i2c_master_dev_handle_t g_dev;
static i2c_master_dev_handle_t g_rom;
static const char* TAG = "PMBus";

void PMBusConfig(uint8_t devAddr, uint8_t romAddr, i2c_master_bus_handle_t bus, uint32_t freq)
{
    g_smbus.address = devAddr;
    g_smbus.romAddr = romAddr;
    g_smbus.bus = bus;
    g_smbus.freq = freq;
    g_smbus.timeout = 50;
    printf("Config Addr[0x%02x] bus[0x%08lx] freq[%lu]\n", devAddr, (uint32_t)bus, freq);
    i2c_device_config_t pmbus = {
        .scl_speed_hz = g_smbus.freq,
        .device_address = g_smbus.address,
    };
    i2c_device_config_t rom = {
        .scl_speed_hz = g_smbus.freq,
        .device_address = g_smbus.romAddr,
    };
    int ret = i2c_master_bus_add_device(g_smbus.bus, &pmbus, &g_dev);
    printf("pmbus add device [0x%02x] %s with 0x%x\n", devAddr, ret == ESP_OK ? "success" : "failed", ret);
    ret = i2c_master_bus_add_device(g_smbus.bus, &rom, &g_rom);
    printf("pmbus add device [0x%02x] %s with 0x%x\n", romAddr, ret == ESP_OK ? "success" : "failed", ret);

    PMBusSyncInfo();
    PMBusSyncStatus();
    PMBusSyncStatusPrint();
}

void PMBusSyncInfo()
{
    PMBusRegReadRom(0x2c, g_smbus.mfrID, sizeof(g_smbus.mfrID)); // mfrID
}

void PMBusSyncStatus()
{
    uint16_t val = 0;
    PMBusRegRead16Bit(0x08, &val); // V_in
    g_smbus.V_in = (float)val / 32.0f;
    PMBusRegRead16Bit(0x0a, &val); // I_in
    g_smbus.I_in = (float)val / 64.0f;
    PMBusRegRead16Bit(0x0e, &val); // V_out
    g_smbus.V_out = (float)val / 256.0f;
    PMBusRegRead16Bit(0x12, &val); // PwrOut
    g_smbus.PwrOut = (float)val;
}

void PMBusSyncStatusPrint()
{
    printf("MFR ID: %s\n", g_smbus.mfrID);
    printf("VoltIn = %.2f, CurrIn = %.2f\n", g_smbus.V_in, g_smbus.I_in);
    printf("VoltOut = %.2f, PwrOut = %.2f\n", g_smbus.V_out, g_smbus.PwrOut);
}

void PMBusRegRead16Bit(uint8_t reg, uint16_t* val)
{
    uint8_t regaddr[2] = {0};
    uint8_t regData[2] = {0};
    regaddr[0] = reg;
    regaddr[1] = ((0xFF - (reg + (g_smbus.address << 1))) + 1) & 0xFF;
    int ret = i2c_master_transmit_receive(g_dev, regaddr, sizeof(regaddr), regData, sizeof(regData), g_smbus.timeout);
    if (ret != ESP_OK) {
        ESP_LOGD(TAG, "pmbus PMBusRegRead16Bit %02x failed with 0x%x\n", reg, ret);
        return;
    }
    *val = regData[1] << 8 | regData[0];
    ESP_LOGD(TAG, "pmbus receiving %s : reg[0x%02x] data[0x%04x]\n", ret == ESP_OK ? "success" : "failed", reg, *val);
}

void PMBusRegRead8Bit(uint8_t reg, uint8_t* val)
{
    uint8_t regaddr[2] = {0};
    regaddr[0] = reg;
    regaddr[1] = ((0xFF - (reg + (g_smbus.address << 1))) + 1) & 0xFF;
    int ret = i2c_master_transmit_receive(g_dev, regaddr, sizeof(regaddr), val, 1, g_smbus.timeout);
    if (ret != ESP_OK) {
        ESP_LOGD(TAG, "pmbus PMBusRegRead8Bit %02x failed with 0x%x\n", reg, ret);
        return;
    }
    ESP_LOGD(TAG, "pmbus receiving %s : reg[0x%02x] data[0x%02x]\n", ret == ESP_OK ? "success" : "failed", reg, *val);
}

void PMBusRegReadRom(uint8_t reg, uint8_t* val, uint8_t len)
{
    uint8_t regaddr[2] = {0};
    regaddr[0] = reg;
    regaddr[1] = ((0xFF - (reg + (g_smbus.address << 1))) + 1) & 0xFF;
    int ret = i2c_master_transmit_receive(g_dev, regaddr, sizeof(regaddr), val, len, g_smbus.timeout);
    if (ret != ESP_OK) {
        ESP_LOGD(TAG, "pmbus PMBusRegReadBlock %02x failed with 0x%x\n", reg, ret);
        return;
    }
    ESP_LOGD(TAG, "pmbus PMBusRegReadBlock receiving %s : reg[0x%x] len[%u] data[%s]\n", ret == ESP_OK ? "success" : "failed", reg, len, val);
}

const smbus_info_t* GetPMBusInfoInstance()
{
    return &g_smbus;
}